using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading;
using Taxhui.Utils.IOUtils;
using Taxhui.Utils.Network.CustomProc;
using Taxhui.Utils.Network.Delegate;
using Taxhui.Utils.Network.Tcp.Awaitable;
using Taxhui.Utils.Network.Tcp.Pooling;
using Taxhui.Utils.Network.Tcp.TcpConfig;
using Taxhui.Utils.Network.UtilityHelper;

namespace Taxhui.Utils.Network.Tcp.NetSession
{
    //Packet模式基类
    public class PackSessionBased : Session, ICustomDataProc
    {
        private readonly byte[] _emptyHeart = new byte[] { 0, 0, 0, 0 };
        private int _dataRecved;
        private readonly bool _iscompress;
        private byte[] _headbuff = new byte[] { };
        internal int _isuchannel = 0;
        internal DateTime HeartTime { get; private set; } = DateTime.Now;

        #region 构造函数

        internal PackSessionBased(
            ConfigBase cfg,
            AwaiterPool awaiterPool,
            SessionPool sessionPool,
            NotifyEventHandler<CompleteNotify, Session> notifyEventHandler,
            LogHelper logHelper,
            NetEngineBased agent,
            ICustomDataProc hdProc = null)
            : base(notifyEventHandler, cfg, awaiterPool, sessionPool, agent, logHelper, hdProc)
        {
            _iscompress = cfg.CompressTransferFromPacket;
        }

        #endregion 构造函数

        #region 更新session状态

        internal override void Attach(Socket socket)
        {
            if (socket == null)
                throw new ArgumentNullException("socket");

            lock (_opsLock)
            {
                HeartTime = DateTime.Now;
                _state = ConnectionState.Connected;
                _socket = socket;
                _startTime = DateTime.UtcNow;
                SetSocketOptions();
            }
        }

        #endregion 更新session状态

        #region 清除session状态

        internal override void Detach()
        {
            lock (_opsLock)
            {
                _socket = null;
                _state = ConnectionState.None;
                AppTokens = null;
                _dataRecved = 0;
            }
        }

        #endregion 清除session状态

        #region 设置套接字参数

        private void SetSocketOptions()
        {
            _socket.ReceiveBufferSize = _configuration.ReceiveBufferSize;
            _socket.SendBufferSize = _configuration.SendBufferSize;
            _socket.ReceiveTimeout = (int)_configuration.ReceiveTimeout.TotalMilliseconds;
            _socket.SendTimeout = (int)_configuration.SendTimeout.TotalMilliseconds;
            _socket.NoDelay = _configuration.NoDelay;

            if (_configuration.KeepAlive)
                SetKeepAlive(_socket, 1, _configuration.KeepAliveInterval, _configuration.KeepAliveSpanTime);
            //不允许利用IP，独占端口
            //_socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, true);
        }

        #endregion 设置套接字参数

        #region 设置在线探测参数

        private void SetKeepAlive(Socket sock, byte op, int interval, int spantime)
        {
            uint dummy = 0;
            byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
            BitConverter.GetBytes(op).CopyTo(inOptionValues, 0);//开启keepalive
            BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多长时间开始第一次探测
            BitConverter.GetBytes((uint)spantime).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探测时间间隔
            sock.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
        }

        #endregion 设置在线探测参数

        #region 开始运行接收数据

        internal override void StartProcess()
        {
            var awaiter = _awaiterPool.Take();
            _headbuff = new byte[1];
            _dataRecved = 0;
            awaiter.Args.SetBuffer(_headbuff, _dataRecved, _headbuff.Length);
            NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
        }

        #endregion 开始运行接收数据

        #region 包头处理

        private void HeadProcess(Awaiter awaiter, SocketError error)
        {
            //状态不正确时直接销毁session
            if (awaiter.Args.BytesTransferred == 0 ||
                error != SocketError.Success ||
                _state != ConnectionState.Connected ||
                _socket == null)
            {
                _logger.WriteLog("接收数据异常，结束会话 state：" + _state.ToString() + " socket_error：" + error.ToString());
                EndTransfer(awaiter);
                return;
            }
            //更新心跳时间
            HeartTime = DateTime.Now;
            //已接收长度
            _dataRecved += awaiter.Args.BytesTransferred;
            int headLen = _headerProc.ResolveHead(_headbuff);
            //判断head超过最大包头长度直接断开连接
            if (_headbuff.Length > _headerProc.GetMaxHeadLen())
            {
                Close(true);
                return;
            }
            //当接收到足够的包头长度时开始处理包
            if (headLen >= 0)
            {
                int bodyLen = headLen;
                //长度包越界判断
                if (bodyLen < 0)
                {
                    EndTransfer(awaiter);
                    return;
                }
                //收到0长度的包
                if (bodyLen == 0)
                {
                    if (_configuration._SessionIsService) //如果是服务端，则反馈心跳包
                        if (_isuchannel == 0)
                        {
                            var h_awaiter = _awaiterPool.Take();

                            //4个字节空包头
                            h_awaiter.Args.SetBuffer(_emptyHeart, 0, _emptyHeart.Length);
                            NetHelper.SendAsync(_socket, h_awaiter, (a, e) => _awaiterPool.Return(a));
                        }

                    //继续接收包头
                    _dataRecved = 0;
                    _headbuff = new byte[1];
                    //设置接收缓冲区
                    awaiter.Args.SetBuffer(_headbuff, _dataRecved, _headbuff.Length);
                    //开始接收，回调函数依然为接收包头，因为收到的是心跳
                    NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
                    return;
                }
                //完整包缓冲区
                _completebuffer = new byte[bodyLen];
                //接收偏移量归0
                _dataRecved = 0;
                //设置接收缓冲区长度
                awaiter.Args.SetBuffer(_completebuffer, _dataRecved, _completebuffer.Length);
                //开始接收，回调函数处理包
                NetHelper.ReceiveAsync(_socket, awaiter, PacketProcess);
            }
            else
            {
                //包头长度不够，继续接收包头长度-已接收长度的数据
                Array.Resize(ref _headbuff, _headbuff.Length + 1);
                awaiter.Args.SetBuffer(_headbuff, _dataRecved, _headbuff.Length - _dataRecved);
                NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
            }
        }

        #endregion 包头处理

        #region 接收包

        private void PacketProcess(Awaiter awaiter, SocketError error)
        {
            int recved = awaiter.Args.BytesTransferred;
            //接收线程出错
            if (recved == 0 ||
                error != SocketError.Success ||
                _state != ConnectionState.Connected ||
                _socket == null)
            {
                _logger.WriteLog("接收出错，结束会话 state：" + _state.ToString() + " socket_error：" + error.ToString());
                EndTransfer(awaiter);
                return;
            }

            ReceiveBytesTransferred = recved;
            //触发通知，正在接收数据
            _notifyEventHandler?.Invoke(CompleteNotify.OnDataReceiveing, this);
            //更新心跳时间
            HeartTime = DateTime.Now;
            //已接收包长度
            _dataRecved += recved;
            //如果已接收长度足够或已超过完整包长度则开始封包
            if (_dataRecved >= _completebuffer.Length)
            {
                //压缩和通知事件
                PackageProcess();
                //收包长度偏移量归0
                _dataRecved = 0;
                //开始接收包头
                _headbuff = new byte[1];
                awaiter.Args.SetBuffer(_headbuff, _dataRecved, _headbuff.Length);
                NetHelper.ReceiveAsync(_socket, awaiter, HeadProcess);
            }
            else
            {
                //不够长度，继续接收
                awaiter.Args.SetBuffer(_dataRecved, _completebuffer.Length - _dataRecved);
                NetHelper.ReceiveAsync(_socket, awaiter, PacketProcess);
            }
        }

        #endregion 接收包

        #region 打包后发送通知

        private void PackageProcess()
        {
            if (_iscompress)
                _completebuffer = DataUtil.Decompress(_completebuffer);
            byte[] packageComplete = _headerProc.ResolvePack(_headbuff, _completebuffer);
            _notifyEventHandler?.Invoke(CompleteNotify.OnDataReceived, this);
        }

        #endregion 打包后发送通知

        #region 关闭传输销毁session

        private void EndTransfer(Awaiter awaiter)
        {
            Close(true);
            _awaiterPool.Return(awaiter);
            _sessionPool.Return(this);
        }

        #endregion 关闭传输销毁session

        #region 发送数据

        public override void SendAsync(byte[] data) => SendAsync(data, 0, data.Length);

        public override void SendAsync(byte[] data, int offset, int lenght)
        {
            if (_socket == null)
                return;
            //根据选项压缩数据
            byte[] buffer = _iscompress
                ? BuilderPack(DataUtil.Compress(data, offset, lenght))
                : BuilderPack(data, offset, lenght);

            var awaiter = _awaiterPool.Take();
            awaiter.Args.SetBuffer(buffer, 0, buffer.Length);

            Interlocked.Increment(ref _isuchannel);
            NetHelper.SendAsync(_socket, awaiter, (a, e) =>
             {
                 Interlocked.Decrement(ref _isuchannel);
                 _awaiterPool.Return(awaiter);
                 SendTransferredBytes = a.Args.Buffer.Length;
                 _notifyEventHandler?.Invoke(CompleteNotify.OnSend, this);
             });
        }

        #endregion 发送数据

        #region 封包

        private byte[] BuilderPack(byte[] data) => BuilderPack(data, 0, data.Length);

        private byte[] BuilderPack(byte[] data, int offset, int length)
        {
            if (!_headerProc.IsAutoHeader())
                return data;
            byte[] buffer = new byte[data.Length + sizeof(int)];
            var bodyLen = BitConverter.GetBytes(data.Length); //数据包长度
            Array.Copy(bodyLen, 0, buffer, 0, bodyLen.Length); //包头4字节
            Array.Copy(data, offset, buffer, 4, length); //body
            return buffer;
        }

        #endregion 封包

        #region 关闭连接

        public override void Close(bool notify)
        {
            lock (_opsLock)
            {
                if (_socket != null)
                {
                    try
                    {
                        _socket.Shutdown(SocketShutdown.Both);
                        _socket.Close();
                    }
                    catch (Exception e)
                    {
                        _logger.WriteLog("session关闭错误 info：" + e.Message);
                    }
                    finally
                    {
                        _socket = null;
                    }
                    _state = ConnectionState.Closed;

                    if (notify)
                    {
                        _notifyEventHandler?.Invoke(CompleteNotify.OnClosed, this);
                    }
                }
            }
        }

        #endregion 关闭连接
    }
}